home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
gnuc25.zip
/
C.E
< prev
Wrap
Text File
|
1992-07-29
|
48KB
|
1,795 lines
// c.e
//
// C code editing commands for Epsilon 6.00
//
// Copyright (C) 1985, 1986, 1987 Free Software Foundation, Inc.
// Copyright (C) 1991, K. Shane Hartman and the Free Software
// Foundation, Inc.
//
// This file was part of GNU Emacs but only superficially resembles
// the original Emacs LISP code.
//
// This file is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 1, or (at your
// option) any later version.
//
// This file is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details (the file COPYING)
//
// If you want a copy of the GNU General Public License, write to the
// Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
//
//
// Most of this code was ported to EEL by me (KSH) from Gnu Emacs
// lisp sources. The remainder was created by me to facilitate the
// port. All code is Copyleft by K. Shane Hartman and the Free
// Software Foundation. See the file COPYING for specific terms of
// the license.
//
// This file is a straight replacement for Lugaru's C.E. Just
// compile load and save state. You may want to play with the
// control variables to get the style you want. See below.
//
// WARNING: The Gnu C indenter is very good, but searches a lot. If
// your machine is wimpy, I would stick with Lugaru's indenter which
// is faster and dumber. I use a 486/33 so I don't notice the extra
// computation. Also, I use a limited lookback parser to handle most
// of the simple cases which assumes the indentation style used in
// this file. The general indenter is used for hairy cases. It is
// disabled by default, but see variable c-indenter-shortcut.
// Without the quick-indenter, large functions require noticeable but
// tolerable time to indent. The reason is that Lugaru's regex
// searches really suck. (07/10/92 JBK Ripped out the lookback
// parser. The performance benefits have become less obvious as the
// general indenter has been tuned over time).
//
// Note the commands (refer to C.DOC or the code for details):
//
// forward_cexp()
// backward_cexp()
// kill_cexp()
// start_of_defun()
// end_of_defun()
// up_level()
// indent_function()
// mark_function()
// list_functions()
//
// Revision History:
//
// Send bug reports, bug fixes to shane@ai.mit.edu
//
// Version 1.0: 06/01/91 shane@ai.mit.edu Initial version.
//
// Version 1.1: 06/20/91 shane@ai.mit.edu Some bug fixes.
//
// Version 1.2: 06/28/91 shane@ai.mit.edu Improve performance of
// looking_at slightly.
//
// Version 1.3: 07/08/91 shane@ai.mit.edu Removed is_word_char,
// use Lugaru's. Also added variable c_tab_always_indent.
//
// Version 1.4: 07/12/91 shane@ai.mit.edu Made C-M-A a little
// smarter.
//
// Version 1.5: 07/19/91 shane@ai.mit.edu Fix problem with
// indenting lines after disgusting c++ comments.
//
// Version 1.6: 07/18/91 More fun with c++ comments. Fix
// c_backward_to_noncomment to understand them.
//
// Version 1.7: 07/18/91 More fun with c++ comments. Fix bug in
// skip_c_comments to understand them. Make
// c_backward_to_start_of_if stop if it detects a syntax error.
// Stop binding case_fold many times. Do it once in outermost
// call and use unwind-protect (C's miserable excuse for it,
// that is).
//
// Version 1.8: 08/05/91 shane@ai.mit.edu Improve performance of
// CR between functions if c_indenter_shortcut is T. Flush
// c_backward_to_start_of_do, I don't think it does anything.
// Do case folding in C-M-Q (indent-function). Halt infinite
// loop for #thing ... \ at file begin. Make the effect of
// c_brace_offset and c_case_offset more predictable for
// settings I don't use. Remove unused variable
// c_continued_brace_offset. Use c_brace_offset for this.
//
// Version 1.9: 08/12/91 johnk@wrq.com (John Kercheval) Fix the
// indent-function command to use spots to remember the region
// it is indenting since the region changes while indenting.
// Use beginning_of_defun to determine the true function start
// rather than the macro BEGINNING_OF_DEFUN(). Broke out
// beginning_of_defun command guts and renamed command
// beginning-of-defun to start-of-defun to support this (the
// command recenters the window). Fix command do-c-indent to
// handle tab correctly when !c_tab_always_indent and epsilon is
// doing space to tab translation. Fix c_indenter_1 to handle
// statement after case correctly (use current_indentation not
// current_column).
//
// 08/12/91 shane@ai.mit.edu Fix indenter to work correctly when
// there are no characters after the insertion point.
//
// Version 1.10: 08/13/91 shane@ai.mit.edu Restore
// c_backward_to_start_of_do. It handles do - while statements
// after ifs, etc.
//
// Version 1.11: 09/03/91 shane@ai.mit.edu CC is C++ file
// extension. CPP is is C++ file extension. Fix bug with foo;
// // comment in c_backward_to_noncomment.
//
// Version 1.12: 11/13/91 shane@ai.mit.edu Had to bind
// do_c_newline to CTRL('M') in Epsilon 5.0.3, spec '\n' no
// longer seems to invoke do-c-newline on enter. Added
// mark-defun (I would put it on C-M-H but Epsilon treats this
// the same as ALT-Backspace which sucks (they must have made
// key handling compatible with ix). Made
// calculate-simple-c-indent default to hairy indenter when it
// detects "foo ( bar \n && ...". Ignore c-brace-offset if
// brace is toplevel.
//
// Version 1.13: 11/14/91 shane@ai.mit.edu Made '{' and '}'
// commands respect auto_indent variable. Changed mode name to
// "NewC" rather than "GnuC". Added command list-defuns on A-#.
// Fixed C++ comment bug reported by Caleb Epstein. Fixed C++
// public: bug noted by mariogo@microsoft.com.
//
// Version 2.00: 07/10/92 johnk@wrq.com (John Kercheval) Remove
// lots of bits and pieces of code hanging around. Use save_var
// and save_spot for the protect unwind code and spot
// allocations. Speed up and reduce size of looking_at(). Port
// to V6.0 Epsilon sources. Removed the quick indent code in
// favor of the full GNU indenter. Renamed list_defuns to
// list_functions and modified to work with old style c function
// declarations.
//
// Version 2.01: 07/20/92 johnk@wrq.com (John Kercheval) Fix bug
// in skip_chars() when at the end of a buffer and called in
// reverse direction. Fix a bug in parse_partial_cexp() dealing
// with C++ style comments (the from and to parameters were
// being ignored during the incomment state boolean release).
// Fixed fundamental bug in do_backward_to_noncomment() which
// prevented correct reverse searches over C++ style comments
// (this included addition of a new state variable to mark the
// beginning of the last comment).
//
// Version 2.02: 07/20/92 johnk@wrq.com (John Kercheval) Fix bug
// in parse_partial_cexp() dealing with C++ style comments (one
// of the edge cases required >= rather than >).
//
// Version 2.03: 07/21/92 johnk@wrq.com (John Kercheval) Add
// several syntax elements which will result in an autoindent on
// newline (was case, labels and a particular else and while
// syntax). Those added were: general while, else, for, if and
// switch syntaxes. Remove unneeded do loop and state query in
// calculate_c_indent(). Fix bug in calculate_c_indent() which
// would result in incorrect indent on first case within a
// switch and incorrectly backed up over continued parameter
// list. Comment indenter module in some detail. Fixed a bug
// in parse_partial_cexp(), the terminating condition on the
// while loop did not take into account multi-character search
// criteria, the point to end location comparison was changed to
// a matchstart to end location comparison. Moved to C++ style
// line comments to aid in clarity and comment fills. Added the
// boolean variable c_line_comment_align to facilitate in-line
// C++ style comment blocks outside of the current indent level
// similar to the built in behavior for standard C style
// comments.
//
// Version 2.04: 07/22/92 johnk@wrq.com (John Kercheval) Fix a
// newly introduced bug within calculate_c_indent(). The new
// variable c_line_comment_align was affecting lines with
// trailing comments as well as full line comments.
//
// Version 2.05: 07/29/92 johnk@wrq.com (John Kercheval) Fix a
// bug in c_indenter_1() for the case where indenting an else
// cuddling a brace. We were backing up one cexp to the opening
// brace when we should have been backing up one more step to
// the opening paren on the if block. Do some more commenting
// and add the EDOC extension (C.DOC).
//
#include "eel.h"
typedef struct pstate {
int incomment;
int beginning_of_comment;
int instring;
int level;
int containing_cexp;
int quoted;
int beginning_of_defun;
} PSTATE;
////////////////////////////////////////////////////////////////////////////
//
// The following variables control indentation style.
//
//
// Indentation of C statements with respect to containing block.
// This is in spaces but is filled with tabs if tab-size is smaller
// than this value or the computed indent. This is the *standard*
// indentation level.
//
int c_indent_level = 4;
//
// Imagined indentation of a C open brace that actually follows a
// statement. This will result in an indentation for braced blocks
// larger than that for continuation lines. This value affects only
// code within the brace, not the brace indent itself.
//
int c_brace_imaginary_offset = 0;
//
// Extra indentation for braces, compared with other text in same
// context. This value increases the depth of the brace. A brace
// offset of 0 will line a brace on the following line at the same
// indent level as the previous statement. This value specifies the
// number of additional spaces to add to this indent.
//
int c_brace_offset = 0;
//
// Indentation level of declarations of C function arguments. This
// specifies the indent level of non-ansi declarations in spaces.
//
int c_argdecl_indent = 4;
//
// Offset of C label lines (including C++ class keywords) relative to
// usual indentation level. This level is normally a negative number.
//
int c_label_offset = -2;
//
// Offset of C case statements relative to usual indentation. The
// case statement will add this indent offset to the case blocks
// within the switch statement. The *usual* indentation is
// c_indent_level as set above.
//
int c_case_offset = 0;
//
// Extra indent for lines not starting new statements such as
// non-braced statements after while, if, for, etc. This does not
// effect function parameters, paren expression, etc. (which are
// lined up with the open paren).
//
int c_continued_statement_offset = 4;
//
// This BOOL value affects the alignment of the indent following a
// line comment (C++). If this value is TRUE then the line following
// a C++ line comment will be indented to be even with the previous
// line if the previous line is also a non-trailing line comment
// (blank lines *are* significant). This facilitates block and fill
// comments which are not at normal indentation levels. If this
// value is FALSE or if there is no non-trailing line comment on the
// previous line then indent will procede normally. This value will
// only affect behavior when a non-trailing comment is on the
// previous line *and* is at a different indentation level than the
// current code block. This is the non-modifiable default behavior
// for C style comments.
//
int c_line_comment_align = 1;
//
// This BOOL value effects the global behavior of the indenter. A
// non-zero value means TAB in C mode should always reindent the
// current regardless of where in the line point is when the TAB
// command is used.
//
int c_tab_always_indent = 0;
////////////////////////////////////////////////////////////////////////////
//
// Code Macros
//
#define LOOKING_AT(pat) looking_at (1, pat)
#define INDENT_TO(indent) to_column(indent)
#define SKIP_CHARS_BACKWARD(set) skip_chars (-1, (set))
#define SKIP_CHARS_FORWARD(set) skip_chars (1, (set))
#define FORWARD_SEXP() traverse_cexp (1)
#define BACKWARD_SEXP() traverse_cexp (-1)
#define SEARCH_FORWARD(str) search (1, (str))
#define SEARCH_BACKWARD(str) search (-1, (str))
#define BOLP() (current_column () == 0)
#define BEGINNING_OF_DEFUN() \
(re_search (-1, "^[{A-Za-z0-9$_]+[^A-Za-z0-9$_:]") ? \
((to_begin_line()), 1) : \
((point = 0), 0))
////////////////////////////////////////////////////////////////////////////
//
// skip_chars will move over all characters in set in the current
// buffer in a particular direction
//
skip_chars(dir, set)
int dir;
char *set;
{
int last = size();
int offset = dir < 0 ? -1 : 0;
while (((point < last) || (dir == -1))
&& point > 0
&& index(set, character(point + offset)))
point += dir;
}
////////////////////////////////////////////////////////////////////////////
//
// current_indentation will return the value of the indentation of
// the current line in the buffer.
//
current_indentation()
{
int indent;
save_var point;
to_indentation();
indent = current_column();
return (indent);
}
////////////////////////////////////////////////////////////////////////////
//
// looking_at will return TRUE if the pattern in the given direction
// is true beginning at the current point.
//
looking_at(dir, pat)
int dir;
char *pat;
{
save_spot point;
if (parse_string(dir, pat, NULL))
return 1;
else
return 0;
}
////////////////////////////////////////////////////////////////////////////
//
// matching_end and associated macros will return the location of the
// closest matching character in the forward direction.
//
char matching_ends[] = "][]}{})()\"\"''";
char opening_ends[] = "[{(";
char closing_ends[] = "]})";
char
matching_end(c)
char c;
{
char *s = index(matching_ends, c);
if (s == NULL)
return (0);
else
return (s[1]);
}
#define opening_end(c) (index (opening_ends, (c)) != NULL)
#define closing_end(c) (index (closing_ends, (c)) != NULL)
////////////////////////////////////////////////////////////////////////////
//
// is_slashified will return TRUE if the current character is a C
// literal escaped by the '\'
//
is_slashified(dir)
int dir;
{
int pos = point;
int slash_count = 0;
if (dir > 0)
pos--;
while (pos-- > 0 && character(pos) == '\\')
slash_count++;
return (slash_count & 1);
}
////////////////////////////////////////////////////////////////////////////
//
// beginning_of_defun will move to the beginning of the current
// function.
//
beginning_of_defun() {
int last = point;
while (BEGINNING_OF_DEFUN()) {
int next = point;
int save_excursion;
if (character(point) == '{') {
while (point > 0) {
nl_reverse();
save_excursion = point;
to_begin_line();
next = point;
SKIP_CHARS_FORWARD(" \t");
if (point == save_excursion) {
nl_forward();
break;
}
point = next;
if (LOOKING_AT("#|(*.%*/)|(//)")) {
nl_forward();
break;
}
}
break;
}
skip_c_comments(-1);
save_excursion = point;
to_begin_line();
if (character(point) == '#') {
point = next;
break;
}
to_end_line();
if (character(point - 1) == '\\') {
point = next;
break;
}
last = next;
point = save_excursion;
if ((character(point) == '}')
|| ((character(point) == ';')
&& (point > 0)
&& ((character(point - 1) == '}')
|| (character(point - 1) == ')')
|| (BACKWARD_SEXP(),
skip_c_comments(-1),
character(point) == '=')))) {
point = last;
break;
}
if (point == 0)
break;
to_end_line();
point++;
}
}
////////////////////////////////////////////////////////////////////////////
//
// skip_c_comments will move over all whitespace and C/C++ comments
// in the given direction starting at point in current buffer.
//
skip_c_comments(dir)
int dir;
{
int offset = 1;
int last = size();
if (dir != 1) {
dir = -1;
offset = 0;
}
while (re_search(dir, "[^ \t\n\f]")
&& point > 0
&& point < last) {
char c = character(point - offset);
char cc = character(point - offset + dir);
if (c == '/') {
if (cc == '*')
search(dir, dir > 0 ? "*/" : "/*");
else if (cc == '/') { /* c++ comment */
if (dir > 0)
nl_forward();
else
point--;
}
} else {
if (dir > 0)
point -= dir;
break;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// traverse_cexp will move in the given direction to the end of the
// current subexpression delineated by some C/C++ delimeter.
//
// KSH - This should use parse_partial_cexp but I did not port some
// of the functionality it should have to do this. This is probably
// faster and I have no more time to devote to this project, so I
// have left it.
//
// JBK - This should indeed use some sort of partial expression
// parser. The current implementation does not handle lone single or
// double quotes or closing C comment characters within C++ style
// line comments when traversing backwards. If such a character
// pattern is found within the line comment then several things may
// result but all will result in incorrect indentation. Speed issues
// and a lack of interest (I just do not generally use these
// character patterns in comments) prevent me from making the
// required modifications. Any volunteers? Wed, 07/29/1992 23:08:40
//
traverse_cexp(dir)
int dir;
{
int level = 0;
int orig = point;
char c = 0;
char start = 0;
char end = 0;
int offset = 1;
char buf[2];
char patbuf[32];
if (dir != 1) {
dir = -1;
offset = 0;
}
if (dir > 0
&& point < size()
&& index(":;?,", character(point)))
point++;
skip_c_comments(dir);
//
// if we are not already on one of the open or close end
// delimiters move to the nearest one in the particular
// direction.
//
if (!index(matching_ends, character(point))) {
if (re_search(dir, "[][)(}{\"';:,? \t\n\f]")) {
point -= dir;
return (1);
}
return (0);
} else if ((opening_end(character(point)) && dir > 0)
|| (closing_end(character(point)) && dir < 0)
|| character(point) == '"'
|| character(point) == '\'') {
//
// Move through code and comments until we get to the next
// c subexpression.
//
if (dir < 0)
point++;
strcpy(patbuf, "[][)(}{\"']|/%*|%*/|//");
while (re_search(dir, patbuf)) {
// here should be parse_partial_cexp() calls
buf[0] = c = character(point - offset);
buf[1] = 0;
if (start == 0) {
start = c;
end = matching_end(start);
if (!end)
start = 0;
else {
strcpy(patbuf, "%X|%X|[\"']|/%*|%*/|//");
patbuf[1] = start;
patbuf[4] = end;
}
}
if (c == '"' || c == '\'') {
char strpat[8];
strcpy(strpat, "%X|\n");
strpat[1] = c;
while (re_search(dir, buf) && is_slashified(dir));
if (character(point - 1) == '\n')
break;
} else if (c == '*') {
search(dir, dir > 0 ? "*/" : "/*");
} else if (c == '/') { /* c++ comment */
if (dir > 0)
nl_forward();
}
if (c == start)
level++;
if (c == end && !--level)
break;
}
return (level == 0 && start != 0);
} else {
point = orig;
return (0);
}
}
////////////////////////////////////////////////////////////////////////////
//
// parse_partial_cexp will move through the region specified by from
// and to and determine the current indent (or block) level and state
// of that region.
//
parse_partial_cexp(from, to, state)
int from;
int to;
PSTATE *state;
{
char c = 0;
int stack[64];
state->instring = 0;
state->incomment = 0;
state->beginning_of_comment = 0;
state->containing_cexp = -1;
state->quoted = 0;
state->level = -1;
point = from;
//
// parse through open and close delimiters until we get to the
// end of the region. Track comments as well as current level
// and level start location.
//
while (re_search(1, "[][(){}\"']|/%*|//") && (matchstart < to)) {
c = character(point - 1);
if (opening_end(c)) {
//
// just store the current level start, error check
// internal stack.
//
state->level++;
if (state->level > (sizeof(stack) / sizeof(stack[0])))
error("Nesting too deep: %d:", state->level);
stack[state->level] = point - 1;
} else if (closing_end(c)) {
//
// decrement the stack level and use the previous
// enclosing level as the current. Validate that the
// level end characters match the opening delimiter.
//
if (state->level >= 0
&& matching_end(character(stack[state->level])) == c)
state->level--;
else if (state->level >= 0) {
point = stack[state->level];
error("Looking for %c", character(stack[state->level]));
}
} else if (c == '"' || c == '\'') {
char strpat[8];
//
// within a string or an escaped character, move along
//
state->quoted = -1;
strcpy(strpat, "[X\n]");
strpat[1] = c;
while (re_search(1, strpat)
&& is_slashified(1)
&& point < to);
if (point < to)
state->quoted = 0;
} else if (c == '*' || c == '/') {
//
// C/C++ comment, pass over the comment and reset state
// if the end of the comment is not past our end limit.
//
state->incomment = 1;
state->beginning_of_comment = point-2;
state->quoted = -2;
if (c == '*') {
SEARCH_FORWARD("*/");
} else {
nl_forward();
}
if (point <= to) {
state->incomment = 0;
state->beginning_of_comment = 0;
state->quoted = 0;
}
}
}
if (state->level >= 0)
state->containing_cexp = stack[state->level];
}
////////////////////////////////////////////////////////////////////////////
//
// c_indenter_1 will indent the current line given an assumption of C
// syntax.
//
c_indenter_1(state)
PSTATE *state;
{
int indent;
int pos;
//
// validate that we have a bit of whitespace at the end of the
// buffer to avoid boundary issues in the rest of the system.
//
if (point == size()) {
insert(' ');
point--;
}
//
// obtain the first best guess for the indent level before we
// special case the current line.
//
pos = size() - point;
indent = calculate_c_indent(state);
to_begin_line();
if (indent == -1)
//
// This is inside a string constant
//
indent = current_indentation();
else if (indent == -2)
//
// This is currently in a comment
//
indent = calculate_c_indent_in_comment();
else if (LOOKING_AT("[ \t]*#"))
//
// We are indenting a preprocessor directive which is always
// at the left edge.
//
indent = 0;
else {
//
// Standard code state, special case particular syntactic
// structures.
//
SKIP_CHARS_FORWARD(" \t");
if (LOOKING_AT("case[ \t'(]|default:")) {
//
// we are in a switch statement and indenting a case
// keyword.
//
if (state->containing_cexp >= 0) {
int save_excursion = point;
point = state->containing_cexp;
indent = current_indentation() +
c_case_offset + c_indent_level;
point = save_excursion;
}
if (indent < 0)
indent = 0;
} else if (LOOKING_AT("[A-Za-z0-9$_]+:")) {
//
// This is a label or C++ class keyword
//
indent += c_label_offset;
if (indent < 0)
indent = 0;
} else if (LOOKING_AT("else[ \t\n]")) {
int save_excursion = point;
//
// This is an else, match to the opening if
//
c_backward_to_start_of_if(state->beginning_of_defun);
indent = current_indentation();
point = save_excursion;
} else if (LOOKING_AT("}[ \t]*else")) {
int save_excursion = point;
//
// This is a cuddly else, we have special knowledge that
// this else lines up with the if with the opening brace.
//
point++;
BACKWARD_SEXP(); // to begin of open brace
BACKWARD_SEXP(); // to begin of open paren
indent = current_indentation();
point = save_excursion;
} else if (LOOKING_AT("while[ \t\n]")) {
int save_excursion = point;
//
// This is a while, check if it is part of a do-while
// loop. If it is not then break to the default else for
// standard indent.
//
if (!c_backward_to_start_of_do(state->beginning_of_defun)) {
point = save_excursion;
goto next; // just use the default else
}
//
// This is a `while' that ends a do-while.
//
indent = current_indentation();
point = save_excursion;
} else
next:
//
// This is a standard C statement, do nothing special unless
// this is an open or close brace.
//
if (character(point) == '}')
indent -= (c_indent_level - c_brace_offset);
else if (character(point) == '{')
indent += c_brace_offset;
}
to_indentation();
//
// Check for the special case of the first or last opening brace
// in the function.
//
if (character(point) == '{'
&& c_brace_offset
&& state->containing_cexp <= 0)
indent -= c_brace_offset;
else if (character(point) == '}') {
int save_excursion = point;
point = state->containing_cexp;
if (BOLP())
indent = 0;
point = save_excursion;
}
//
// do the indent only if we are not already there
//
if (current_column() != indent)
INDENT_TO(indent);
//
// If initial point was within line's indentation, position after
// the indentation. Else stay at same point in text.
//
if (size() - pos > point)
point = size() - pos;
return (indent);
}
////////////////////////////////////////////////////////////////////////////
//
// c_indenter sets up the protect unwind code, inits the state
// variable and calls the primary indenter.
//
c_indenter()
{
PSTATE state;
jmp_buf this_level;
jmp_buf *old_level = top_level;
int ret;
save_var case_fold;
case_fold = 0;
top_level = &this_level;
if (ret = setjmp(top_level)) {
restore_vars();
top_level = old_level;
longjmp(top_level, ret);
}
state.beginning_of_defun = -1;
ret = c_indenter_1(&state);
top_level = old_level;
return ret;
}
////////////////////////////////////////////////////////////////////////////
//
// c_backward_to_exp_start will move to the first character of the
// line containing the beginning of the current sub expression.
//
c_backward_to_exp_start(lim)
int lim;
{
if (index(")\"", character(point - 1)))
BACKWARD_SEXP();
to_begin_line();
if (point <= lim)
point = lim + 1;
SKIP_CHARS_FORWARD(" \t");
}
////////////////////////////////////////////////////////////////////////////
//
// c_backward_to_start_of_do will if point follows a `do' statement,
// move to beginning of it and return TRUE. Otherwise return FALSE
// and don't move point.
//
c_backward_to_start_of_do(limit)
int limit;
{
int save_excursion;
int first = 1;
int startpos = point;
int done = 0;
if (limit < 0)
limit = 0;
//
// loop until we find an open do or move out of the current
// enclosing c expression.
//
while (!done) {
c_backward_to_noncomment(limit);
if (!BACKWARD_SEXP())
//
// We are at the top of the buffer, return the start
// position and bug out.
//
done = 2;
else if (LOOKING_AT("do[^A-Za-z0-9_$]"))
//
// We found an open do
//
done = 1;
else {
//
// Otherwise, if we skipped a semicolon, we lose.
// (Exception: we can skip one semicolon before getting
// to the last token of the statement, unless that token
// is a close brace).
//
save_excursion = point;
if (FORWARD_SEXP()) {
if (!first && character(point - 1) == '}')
done = 2;
if (!done && character(point) == ';' &&
character(point - 1) == '}')
done = 2;
if (!done && character(point) == ';') {
if (first) {
if (character(point - 1) == ')' && BACKWARD_SEXP()) {
if (BACKWARD_SEXP() &&
LOOKING_AT("while") &&
c_backward_to_start_of_do(limit))
continue;
}
}
if (!first)
done = 2;
first = 0;
}
point = save_excursion;
}
}
//
// If we go too far back in the buffer, we lose.
//
if (point < limit && !done)
done = 2;
}
if (done != 1)
point = startpos;
return (done == 1);
}
////////////////////////////////////////////////////////////////////////////
//
// c_backward_to_start_of_if will move to the start of the last
// "unbalanced" `if'.
//
c_backward_to_start_of_if(limit)
int limit;
{
int if_level = 1;
if (limit < 0)
limit = 0;
while (point > 0 && if_level > 0) {
if (!BACKWARD_SEXP()) {
say("Syntax error");
break;
}
if (LOOKING_AT("else[^A-Za-z0-9$_]"))
if_level++;
else if (LOOKING_AT("if[^A-Za-z0-9$_]"))
if_level--;
else if (point < limit) {
if_level = 0;
point = limit;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// c_backward_to_noncomment will move in the reverse direction over
// all whitespace until it runs into some *real* code.
//
c_backward_to_noncomment(lim)
int lim;
{
int opoint = 0;
int stop = 0;
if (lim < 0)
lim = 0;
while (!stop) {
//
// move over the trailing whitespace
//
SKIP_CHARS_BACKWARD(" \t\n\f");
opoint = point;
//
// move through standard comments
//
if (point >= 2 + lim) {
point -= 2;
if (LOOKING_AT("%*/")) {
if (!SEARCH_BACKWARD("/*"))
point = lim > 0 ? lim : 0;
continue;
}
point += 2;
}
if (point <= lim)
stop = 1;
else {
//
// Check for C++ style comment
//
to_begin_line();
SKIP_CHARS_FORWARD(" \t");
if ((character(point) != '#') && !LOOKING_AT("//")) {
PSTATE ps;
int from = point;
int to;
to_end_line();
to = point;
point = from;
parse_partial_cexp(from, to, &ps);
stop = 1;
if (ps.incomment) {
point = ps.beginning_of_comment;
SKIP_CHARS_BACKWARD(" \t");
continue;
}
}
point = opoint;
if (!stop) {
to_begin_line();
}
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// calculate_c_indent_in_comment will return the appropriate indent
// for this comment line. This is special cased for block comments.
//
calculate_c_indent_in_comment()
{
int indent = 0;
save_var point;
to_begin_line();
if (point > 0) {
SKIP_CHARS_BACKWARD(" \t\n");
to_begin_line();
SKIP_CHARS_FORWARD(" \t");
indent = current_column();
if (LOOKING_AT("/%*"))
indent++;
}
return (indent);
}
////////////////////////////////////////////////////////////////////////////
//
// calculate_c_indent will return the appropriate indentation for the
// current line as C code. In usual case an integer is returned: the
// column to indent to. Returns -1 if line starts inside a string,
// -2 if in a comment.
//
calculate_c_indent(state)
PSTATE *state;
{
int parse_start = 0;
int indent_point;
int ret = 0;
int containing_cexp = -1;
PSTATE ps;
save_var point;
//
// init
//
if (!state) {
state = &ps;
state->beginning_of_defun = -1;
}
state->containing_cexp = containing_cexp;
to_begin_line();
//
// Check for previous line comment
//
if (c_line_comment_align) {
int save_excursion_1 = point;
if (nl_reverse()) {
to_begin_line();
if (LOOKING_AT("[ \t]*//")) {
to_indentation();
return current_column();
}
}
point = save_excursion_1;
}
if (point > 0) {
//
// parse to the beginning of this function
//
indent_point = point;
if (state->beginning_of_defun == -1) {
BEGINNING_OF_DEFUN();
state->beginning_of_defun = point;
}
//
// determine the current level and save the point of the
// current enclosing C expression
//
point = state->beginning_of_defun;
ret = 0;
parse_start = point;
parse_partial_cexp(point, indent_point, state);
containing_cexp = state->containing_cexp;
if (state->instring || state->incomment) {
//
// return -1 or -2 if this line is part of a standard C style
// comment block.
//
ret = state->quoted;
} else if (containing_cexp < 0) {
//
// Line is at top level. May be data or function
// definition, or may be function argument declaration.
// Indent like the previous top level line unless that
// ends in a closeparen without semicolon, in which case
// this line is the first argument decl.
//
point = indent_point;
SKIP_CHARS_FORWARD(" \t");
if (character(point) == '{')
ret = 0; /* Unless it starts a function body */
else if (c_argdecl_indent > 0) {
//
// Check for non-ansi argument declarations
//
c_backward_to_noncomment(parse_start > 0 ? parse_start : 0);
//
// look at previous line that's at column 0 to
// determine whether we are in top-level decls or
// function's arg decls.
//
re_search(-1, "^[^ \f\t\n#]");
if (LOOKING_AT("[A-Za-z0-9$_]+[^\"\n=]*%(")) {
point = matchend - 1;
FORWARD_SEXP();
SKIP_CHARS_FORWARD(" \t\f");
if (point < indent_point
&& !index(",;", character(point)))
ret = c_argdecl_indent;
}
}
} else if (character(containing_cexp) != '{') {
//
// line is expression, not statement: indent to just
// after the surrounding open.
//
point = containing_cexp + 1;
ret = current_column();
} else {
//
// Statement level. Is it a continuation or a new
// statement Find previous non-comment character.
//
point = indent_point;
c_backward_to_noncomment(containing_cexp);
//
// Back up over previous parameter continuations since
// they don't affect the continuation indent.
//
while (character(point-1) == ',' && (point > containing_cexp)) {
BACKWARD_SEXP();
to_begin_line();
c_backward_to_noncomment(containing_cexp);
}
//
// Back up over label lines since they don't affect
// whether our line is a continuation. Do not back up
// over case lines or C++ class keywords.
//
{
int stop = 0;
int save_excursion_1 = point;
while (character(point-1) == ':' &&
!stop && (point > containing_cexp)) {
to_indentation();
if (LOOKING_AT("case"))
stop = 1;
else {
c_backward_to_noncomment(containing_cexp);
save_excursion_1 = point;
}
}
point = save_excursion_1;
}
//
// Check for a preprocessor statement or its continuation
// lines. Move back to end of previous non-preprocessor
// line.
//
{
int found = point;
int stop = 0;
int save_excursion_1;
while (!stop) {
save_excursion_1 = point;
if (point > 0)
to_end_line();
if ((character(point - 1)) != '\\') {
point = save_excursion_1;
//
// This line is not preceded by a backslash.
// So either it starts a preprocessor command
// or any following continuation lines should
// not be skipped.
//
to_indentation();
if (character(point) == '#') {
to_end_line();
found = point;
} else
stop = 1;
} else
nl_reverse();
}
point = found;
//
// Now we get the answer.
//
save_excursion_1 = point;
point = indent_point;
SKIP_CHARS_FORWARD(" \t");
//
// Don't treat a line with a close-brace as a
// continuation. It is probably the end of an enum,
// struct, union or other type declaration.
//
if ((character(point)) != '}') {
point = save_excursion_1;
if ((point > 0) && !index(",;{}", character(point - 1))) {
//
// This line is continuation of preceding
// line's statement indent
// c_continued_statement_offset more than the
// previous line of the statement.
//
c_backward_to_exp_start(containing_cexp);
ret = current_column();
point = indent_point;
SKIP_CHARS_FORWARD(" \t");
if (character(point) != '{')
ret += c_continued_statement_offset;
} else
goto new_statement;
} else
new_statement:
{
//
// This line starts a new statement. Position
// following last unclosed open.
//
point = containing_cexp;
//
// Is line first statement after an open-brace or
// after a case. If no, find that first statement
// and indent like it.
//
point++;
{
int colon_line_end = 0;
while (SKIP_CHARS_FORWARD(" \t\n"),
LOOKING_AT("#|/%*|case[ \t\n'(].*:|[a-zA-Z0-9_$]*:")) {
//
// Skip over comments and labels
// following openbrace.
//
if (character(point) == '#')
nl_forward();
else if (character(point) == '/') {
point += 2;
SEARCH_FORWARD("*/");
} else {
//
// case, C++ class keyword or label:
//
save_excursion_1 = point;
to_end_line();
colon_line_end = point;
point = save_excursion_1;
SEARCH_FORWARD(":");
}
}
//
// The first following code counts if it is
// before the line we want to indent.
//
if (point < indent_point
&& colon_line_end > 0
&& colon_line_end < indent_point) {
if (colon_line_end > point)
ret = current_indentation() - c_label_offset;
else if (colon_line_end > 0
&& colon_line_end > containing_cexp) {
save_excursion_1 = point;
point = colon_line_end;
to_indentation();
//
// chack for switch syntax element
//
if (LOOKING_AT("case[ \t'(]|default:")) {
ret = current_column();
point = indent_point;
SKIP_CHARS_FORWARD(" \t");
if (!LOOKING_AT("[{}]"))
ret += c_indent_level;
else
ret -= (c_case_offset +
c_brace_offset);
point = save_excursion_1;
} else {
point = save_excursion_1;
ret = current_column();
}
} else
goto no_previous;
} else
no_previous:
{
//
// If no previous statement, indent it
// relative to line brace is on (or the
// last case statement). For open brace
// in column zero, don't let statement
// start there too. If c_indent_level is
// zero, use c_brace_offset +
// c_continued_statement_offset instead.
// For open-braces not the first thing in
// a line, add in
// c_brace_imaginary_offset.
//
point = containing_cexp;
if (BOLP() && !c_indent_level)
ret = c_brace_offset +
c_continued_statement_offset;
else if (BOLP())
ret = c_indent_level;
else
ret = c_indent_level - c_brace_offset;
//
// Move back over whitespace before the
// openbrace. If openbrace is not first
// nonwhite thing on the line, add the
// c_brace_imaginary_offset.
//
SKIP_CHARS_BACKWARD(" \t");
if (!BOLP())
ret += c_brace_imaginary_offset;
//
// If the openbrace is preceded by a
// parenthesized exp, move to the
// beginning of that; possibly a
// different line.
//
if (character(point - 1) == ')')
BACKWARD_SEXP();
//
// Get initial indentation of current line.
//
ret += current_indentation();
}
}
}
}
}
}
return (ret);
}
////////////////////////////////////////////////////////////////////////////
//
// Epsilon Commands
//
//
keytable c_tab; /* key table for c mode */
char c_mode_name[] = "NewC";
////////////////////////////////////////////////////////////////////////////
//
// c_mode sets up the mode and correctly sets comment hooks
//
command
c_mode()
{
mode_keys = c_tab; /* use these keys */
c_tab[')'] = c_tab[']'] = Matchdelim ? (short) show_matching_delimiter : 0;
indenter = c_indenter;
auto_indent = 1;
major_mode = c_mode_name;
strcpy(comment_start, "(/</|*>)[ \t]*");
strcpy(comment_pattern, "//.*$|/<*>(.|<newline>)*<*>/<FirstEnd>");
if (new_c_comments) {
strcpy(comment_begin, "// ");
strcpy(comment_end, "");
} else {
strcpy(comment_begin, "/* ");
strcpy(comment_end, " */");
}
try_calling("c-mode-hook");
make_mode();
}
////////////////////////////////////////////////////////////////////////////
//
// do_c_indent is a standard tab indent. Repeated tabs will add tabs
// into the current location rather than repeating c_indent on every tab.
//
do_c_indent()
on c_tab[CTRL('I')]
{
int save_excursion = point;
if (!c_tab_always_indent) {
if (prev_cmd == C_INDENT) /* repeated, make bigger */
to_column(current_column() + tab_size -
current_column() % tab_size);
else {
/* If not in indentation, do a tab */
SKIP_CHARS_BACKWARD(" \t");
if (!BOLP()) {
point = save_excursion;
to_column(current_column() + tab_size -
current_column() % tab_size);
return;
}
point = save_excursion;
c_indenter();
}
} else
c_indenter();
this_cmd = C_INDENT;
}
////////////////////////////////////////////////////////////////////////////
//
// do_c_newline is the enter hook. This routine will reindent if the
// current line is one of several syntax block begins.
//
do_c_newline()
on c_tab[CTRL('M')], c_tab[CTRL('J')]
{
spot save_excursion = alloc_spot();
if (auto_indent) {
to_indentation();
if (LOOKING_AT("#|((case|else)[ \t]*)|[A-Za-z0-9$_]+:|((while|if|switch|for)[ \t]*%(.*%))"))
c_indenter();
point = *save_excursion;
free_spot(save_excursion);
insert('\n');
c_indenter();
} else
insert('\n');
}
////////////////////////////////////////////////////////////////////////////
//
// c_open and c_close are the open and close block hooks
//
c_open()
on c_tab['{']
{
normal_character();
if (auto_indent)
c_indenter();
}
c_close()
on c_tab['}']
{
normal_character();
if (auto_indent)
c_indenter();
if (Matchdelim)
find_delimiter();
}
////////////////////////////////////////////////////////////////////////////
//
// forward_cexp and backward_cexp will move to the next (or previous)
// c_exp in the current function.
//
command
forward_cexp()
on c_tab[ALT(CTRL('F'))]
{
int start = point;
int last = size();
if (!traverse_cexp(iter < 0 ? -1 : 1)) {
point = start;
quick_abort();
}
while (index(",:;\\", character(point)) && (point < last))
point++;
}
command
backward_cexp()
on c_tab[ALT(CTRL('B'))]
{
int start = point;
while ((point > 0) && index(",:;\\ \t\n\f", character(point - 1)))
point--;
if (!traverse_cexp(iter < 0 ? 1 : -1)) {
point = start;
quick_abort();
}
}
////////////////////////////////////////////////////////////////////////////
//
// kill_cexp will remove the cexp at the current cursor
//
command
kill_cexp()
on c_tab[ALT(CTRL('K'))]
{
int start = point;
int end;
if (!traverse_cexp(iter < 0 ? -1 : 1)) {
point = start;
quick_abort();
}
end = point;
if (end < start) {
point = end;
end = start;
start = point;
} else
point = start;
iter = 0;
do_save_kill(start, end);
}
////////////////////////////////////////////////////////////////////////////
//
// start_of_defun and end_of_defun will move to the beginning (or
// end) of the current function.
//
command
start_of_defun()
on c_tab[ALT(CTRL('A'))]
{
iter = 0;
beginning_of_defun();
window_start = prev_screen_line(3);
}
command
end_of_defun()
on c_tab[ALT(CTRL('E'))]
{
int orig = point;
iter = 0;
FORWARD_SEXP();
point++;
if (BEGINNING_OF_DEFUN()) {
while (FORWARD_SEXP() && (character(point - 1) != '}'));
if (character(point - 1) != '}')
point = orig;
else if (character(point) == ';')
point++;
}
}
////////////////////////////////////////////////////////////////////////////
//
// up_level will move up one enclosing C block in the current function
//
command
up_level()
on c_tab[ALT(CTRL('U'))]
{
PSTATE state;
int orig = point;
iter = 0;
BEGINNING_OF_DEFUN();
while (point <= orig) {
parse_partial_cexp(point, orig + 1, &state);
}
if (state.containing_cexp >= 0)
point = state.containing_cexp;
else {
point = orig;
error("No containing cexp");
}
}
////////////////////////////////////////////////////////////////////////////
//
// indent_function will reformat the current function by indenting
// every line from the beginning of the function.
//
command
indent_function()
on c_tab[ALT(CTRL('Q'))]
{
PSTATE ps;
jmp_buf this_level;
jmp_buf *old_level = top_level;
int ret;
spot stop = 0;
save_spot point;
save_var case_fold;
iter = 0;
top_level = &this_level;
case_fold = 0;
if (ret = setjmp(top_level)) {
if (stop)
free_spot(stop);
restore_vars();
top_level = old_level;
longjmp(top_level, ret);
}
beginning_of_defun();
ps.beginning_of_defun = point;
end_of_defun();
stop = alloc_spot();
point = ps.beginning_of_defun;
while (point < *stop) {
c_indenter_1(&ps);
nl_forward();
check_abort();
}
free_spot(stop);
top_level = old_level;
this_cmd = 0;
}
////////////////////////////////////////////////////////////////////////////
//
// mark_function will highlight the current function
//
command
mark_function()
{
iter = 0;
beginning_of_defun();
set_mark();
end_of_defun();
exchange_point_and_mark();
}
////////////////////////////////////////////////////////////////////////////
//
// list_functions will show all functions within the current file.
//
#define FUNCTION_BUFFER "*Functions*"
command
list_functions()
on c_tab[ALT('#')]
{
char buff[512];
int brace;
int thisbuf = bufnum;
int funcbuf;
int oldpoint = point;
iter = 0;
point = 0;
funcbuf = zap(FUNCTION_BUFFER);
while (SEARCH_FORWARD("\n{")) {
brace = point;
point++;
beginning_of_defun();
if (SEARCH_FORWARD("(")) {
if (point < brace) {
point--;
re_search(-1, "[a-zA-Z0-9_]+");
grab(matchstart, matchend, buff);
bufnum = funcbuf;
bprintf("%s\n", buff);
bufnum = thisbuf;
}
}
point = brace;
end_of_defun();
}
point = oldpoint;
sort_another(FUNCTION_BUFFER, 0, 0);
view_buffer(FUNCTION_BUFFER, 1);
}